/*
 * Copyright (C) 2007  Vianney le Clément
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package fsahoraires.gui;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.*;

import fsahoraires.cours.*;
import fsahoraires.gui.pages.*;
import fsahoraires.programmes.*;

/**
 * Classe pour gérer l'arbre de gauche et le lien avec les tabs
 */
public class TreeManager {

	private final MainWindow main;
	private final Tree tree;
	private final CTabFolder tabs;

	private boolean showCache = false;

	public TreeManager(MainWindow main, Tree tree, CTabFolder tabs) {
		this.main = main;
		this.tree = tree;
		this.tabs = tabs;

		/*
		 * le data contient un boolean indiquant s'il faut appelé showing sur
		 * les pages
		 */
		tabs.setData(true);

		// listeners
		tree.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				/*
				 * Le data de chaque TreeItem contient un tableau de pages à
				 * afficher
				 */
				setPages((ItemData) e.item.getData());
			}
		});
		tabs.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event event) {
				if ((Boolean) event.widget.getData()) {
					((Page) ((CTabItem) event.item).getData()).update();
					((ItemData) TreeManager.this.tree.getSelection()[0]
							.getData()).setSelection(TreeManager.this.tabs
							.getSelectionIndex());
				}
			}
		});
	}

	private TreeItem createTreeItem(TreeItem parent, int index, String text,
			String image, ItemData data) {
		TreeItem ti = new TreeItem(parent, SWT.NONE, index);
		ti.setText(text);
		ti.setImage(main.loadImage(image));
		ti.setData(data);
		return ti;
	}

	private TreeItem createSemestreItem(TreeItem parent, Semestre semestre,
			Programme programme) {
		int index = 0;
		for (TreeItem ti : parent.getItems()) {
			if (((SemestreData) ti.getData()).getSemestre().compareTo(semestre) > 0)
				break;
			index++;
		}
		return createTreeItem(parent, index, semestre.toString(),
				"semestre.png", new SemestreData(new Page[] {
						new SemestrePage(programme, semestre),
						new SemestreCoursPage(main, semestre),
						new HoraireSemestrePage(semestre) }, semestre));
	}

	private TreeItem createCoursItem(TreeItem parent, Cours cours) {
		int index = 0;
		for (TreeItem ti : parent.getItems()) {
			if (((CoursData) ti.getData()).getCours().compareTo(cours) > 0)
				break;
			index++;
		}
		return createTreeItem(parent, index, cours.toString(), "cours.png",
				new CoursData(new Page[] {
						new CoursPage(cours),
						new SimpleUrlPage("Description", "description.png",
								cours.getDescription().toExternalForm()),
						new HoraireCoursPage(cours, 1),
						new HoraireCoursPage(cours, 2) }, cours));
	}

	private TreeItem findSemestre(Semestre semestre) {
		for (TreeItem ti : tree.getItem(0).getItems()) {
			if (((SemestreData) ti.getData()).getSemestre().equals(semestre))
				return ti;
		}
		return null;
	}

	private TreeItem findCours(Semestre semestre, Cours cours) {
		TreeItem sem = findSemestre(semestre);
		if (sem == null)
			return null;
		for (TreeItem ti : sem.getItems()) {
			if (((CoursData) ti.getData()).getCours().equals(cours))
				return ti;
		}
		return null;
	}

	private TreeItem findCoursInCache(Cours cours) {
		if (!showCache)
			return null;
		for (TreeItem ti : tree.getItem(1).getItems()) {
			if (((CoursData) ti.getData()).getCours().equals(cours))
				return ti;
		}
		return null;
	}

	/**
	 * Définit les tabs à afficher
	 * 
	 * @param pages
	 */
	private void setPages(ItemData data) {
		Page[] pages = data.getPages();
		tabs.setData(false);
		for (CTabItem tab : tabs.getItems()) {
			tab.getControl().dispose();
			tab.setControl(null);
			tab.dispose();
		}
		for (Page page : pages) {
			CTabItem tab = new CTabItem(tabs, SWT.NONE);
			tab.setText(page.getTitle());
			tab.setImage(main.loadImage(page.getImage()));
			tab.setControl(page.createControl(tabs));
			tab.setData(page);
		}
		tabs.setData(true);
		if (pages.length > 0) {
			tabs.setSelection(data.getSelection());
			pages[data.getSelection()].update();
		}
	}

	private void select(TreeItem item) {
		tree.setSelection(item);
		setPages((ItemData) item.getData());
	}

	private void updatePage() {
		CTabItem tab = tabs.getSelection();
		if (tab != null)
			((Page) tab.getData()).update();
	}

	/**
	 * Recrée tout l'arbre, p.e. à l'issue du chargement d'un fichier. Doit être
	 * appelé à l'ouverture d'un fichier (ou la création d'un nouveau fichier).
	 */
	public void rebuildAll() {
		tree.removeAll();

		Programme programme = main.getProgramme();

		TreeItem root = new TreeItem(tree, SWT.NONE);
		root.setText("Programme");
		root.setImage(main.loadImage("programme.png"));
		root.setData(new ItemData(new Page[] { new RootPage(programme),
				new RootSemestresPage(main) }));

		for (Semestre semestre : programme) {
			TreeItem sem = createSemestreItem(root, semestre, programme);
			for (Cours cours : semestre) {
				createCoursItem(sem, cours);
			}
		}

		if (showCache)
			buildCache();

		root.setExpanded(true);
		for (TreeItem child : root.getItems())
			child.setExpanded(true);
		select(root);

		// listeners sur le programme
		final SemestreListener semListener = new SemestreListener() {
			public void removed(Semestre semestre, Cours cours) {
				removeCours(semestre, cours);
			}

			public void added(Semestre semestre, Cours cours) {
				addCours(semestre, cours);
			}
		};
		for (Semestre sem : programme)
			sem.addSemestreListener(semListener);
		programme.addProgrammeListener(new ProgrammeListener() {
			public void removed(Programme programme, Semestre semestre) {
				removeSemestre(semestre);
			}

			public void added(Programme programme, Semestre semestre) {
				addSemestre(semestre);
				semestre.addSemestreListener(semListener);
			}
		});
		programme.getManager().addCoursManagerListener(
				new CoursManagerAdapter() {
					public void modified(final Cours cours) {
						main.asyncExec(new Runnable() {
							public void run() {
								if (tree.isDisposed())
									return;
								for (TreeItem s : tree.getItem(0).getItems()) {
									for (TreeItem c : s.getItems()) {
										if (((CoursData) c.getData())
												.getCours().equals(cours))
											c.setText(cours.toString());
									}
								}
								if (showCache) {
									for (TreeItem c : tree.getItem(1)
											.getItems()) {
										if (((CoursData) c.getData())
												.getCours().equals(cours))
											c.setText(cours.toString());
									}
								}
								main.touchModified();
								updatePage();
							}
						});
					}

					public void added(Cours cours) {
						if (showCache)
							addCoursToCache(cours);
					}

					public void disposed(Cours cours) {
						if (showCache)
							removeCoursFromCache(cours);
					}
				});
		programme.getManager().getFetcher().addCoursFetcherListener(
				new CoursFetcherListener() {
					public void end(Cours cours, boolean description,
							boolean horaires) {
						main.setStatus("Cours '" + cours.getCode()
								+ "' récupéré.");
					}

					public void begin(Cours cours, boolean description,
							boolean horaires) {
						main.setStatus("Récupération du cours '"
								+ cours.getCode() + "'...");
					}
				});
	}

	/**
	 * Ajoute la cache au tree
	 */
	private void buildCache() {
		CoursManager manager = main.getProgramme().getManager();

		TreeItem cache = new TreeItem(tree, SWT.NONE);
		cache.setText("Cache");
		cache.setImage(main.loadImage("cache.png"));
		cache.setData(new ItemData(new Page[] { new SemestreCoursPage(main,
				null) }));

		for (Cours c : manager) {
			createCoursItem(cache, c);
		}
	}

	public boolean isCacheShown() {
		return showCache;
	}

	public void showCache(boolean show) {
		if (show != showCache) {
			showCache = show;
			if (show) {
				buildCache();
			} else {
				TreeItem ti = tree.getItem(1);
				TreeItem sel = tree.getSelection()[0];
				if (sel == ti || sel.getParentItem() == ti)
					select(tree.getItem(0));
				ti.dispose();
			}
		}
	}

	private void addSemestre(Semestre semestre) {
		tree.showItem(createSemestreItem(tree.getItem(0), semestre, main
				.getProgramme()));
		main.touchModified();
		updatePage();
	}

	private void removeSemestre(Semestre semestre) {
		TreeItem ti = findSemestre(semestre);
		if (ti != null) {
			TreeItem sel = tree.getSelection()[0];
			ti.dispose();
			main.touchModified();
			if (ti == sel)
				select(tree.getItem(0));
			else
				updatePage();
		}
	}

	private void addCours(Semestre semestre, Cours cours) {
		TreeItem ti = findSemestre(semestre);
		if (ti != null)
			tree.showItem(createCoursItem(ti, cours));
		main.touchModified();
		updatePage();
	}

	private void addCoursToCache(Cours cours) {
		if (!showCache)
			return;
		TreeItem ti = findCoursInCache(cours);
		if (ti == null)
			createCoursItem(tree.getItem(1), cours);
		main.touchModified();
		updatePage();
	}

	private void removeCours(Semestre semestre, Cours cours) {
		TreeItem ti = findCours(semestre, cours);
		if (ti != null) {
			TreeItem sel = tree.getSelection()[0];
			ti.dispose();
			main.touchModified();
			if (ti == sel)
				select(findSemestre(semestre));
			else
				updatePage();
		}
	}

	private void removeCoursFromCache(Cours cours) {
		TreeItem ti = findCoursInCache(cours);
		if (ti != null) {
			TreeItem sel = tree.getSelection()[0];
			ti.dispose();
			main.touchModified();
			if (ti == sel)
				select(tree.getItem(1));
			else
				updatePage();
		}
	}

	private class ItemData {
		private Page[] pages;
		private int selection;

		public ItemData(Page[] pages) {
			this.pages = pages;
			this.selection = 0;
		}

		public Page[] getPages() {
			return pages;
		}

		public int getSelection() {
			return selection;
		}

		public void setSelection(int selection) {
			this.selection = selection;
		}
	}

	private class SemestreData extends ItemData {
		private Semestre semestre;

		public SemestreData(Page[] pages, Semestre semestre) {
			super(pages);
			this.semestre = semestre;
		}

		public Semestre getSemestre() {
			return semestre;
		}
	}

	private class CoursData extends ItemData {
		private Cours cours;

		public CoursData(Page[] pages, Cours cours) {
			super(pages);
			this.cours = cours;
		}

		public Cours getCours() {
			return cours;
		}
	}

}
